package com.kamistoat.meimeimall.meimeimallpayment.service.impl;

import com.kamistoat.meimeimall.common.To.paymentTo.ReconciliationTaskTo;
import com.kamistoat.meimeimall.common.constant.DatabaseConstant;
import com.kamistoat.meimeimall.common.constant.PaymentConstant;
import com.kamistoat.meimeimall.common.exception.BizCodeEnum;
import com.kamistoat.meimeimall.common.exception.RRException;
import com.kamistoat.meimeimall.common.utils.SNGenerate;
import com.kamistoat.meimeimall.meimeimallpayment.dao.*;
import com.kamistoat.meimeimall.meimeimallpayment.entity.BillRecordEntity;
import com.kamistoat.meimeimall.meimeimallpayment.entity.PaymentRecordEntity;
import com.kamistoat.meimeimall.meimeimallpayment.entity.ReconciliationTaskEntity;
import com.kamistoat.meimeimall.meimeimallpayment.service.BillRecordService;
import com.kamistoat.meimeimall.meimeimallpayment.service.PaymentBillJoinService;
import com.kamistoat.meimeimall.meimeimallpayment.service.PaymentRecordService;
import com.kamistoat.meimeimall.meimeimallpayment.service.ReconciliationTaskService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;


@Service("reconciliationTaskService")
public class ReconciliationTaskServiceImpl implements ReconciliationTaskService {
    protected Logger logger = LoggerFactory.getLogger(ReconciliationTaskServiceImpl.class);

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    ReconciliationTaskDao reconciliationTaskDao;

    @Autowired
    BillRecordService billRecordService;
    @Autowired
    PaymentBillJoinService paymentBillJoinService;
    @Autowired
    PaymentRecordDao paymentRecordDao;
    @Autowired
    BillRecordDao billRecordDao;
    @Autowired
    PaymentBillJoinDao paymentBillJoinDao;
    @Autowired
    ReconciliationErrorDao reconciliationErrorDao;


    /**
     * 创建新的对账任务
     *
     * @param reconciliationTaskTo 对账任务To类
     */
    @Override
    @Transactional
    public void newReconciliationTask(ReconciliationTaskTo reconciliationTaskTo) {
        /**
         * 对账任务起止时间验证
         */
        if (reconciliationTaskTo.getStartTime().after(reconciliationTaskTo.getEndTime())) {
            throw new RRException(BizCodeEnum.ERROR_RECONCILIATION_STARTEND_TIME.getMessage(), BizCodeEnum.ERROR_RECONCILIATION_STARTEND_TIME.getCode());
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String startClock = sdf.format(reconciliationTaskTo.getStartTime()).split(" ")[1];
        String endClock = sdf.format(reconciliationTaskTo.getEndTime()).split(" ")[1];
        if (!startClock.equals("00:00:00") || !endClock.equals("00:00:00")) {
            throw new RRException(BizCodeEnum.ERROR_RECONCILIATION_STARTEND_TIME.getMessage(), BizCodeEnum.ERROR_RECONCILIATION_STARTEND_TIME.getCode());
        }

        ReconciliationTaskEntity reconciliationTaskEntity = new ReconciliationTaskEntity();
        reconciliationTaskEntity.setTaskName(reconciliationTaskTo.getTaskName());
        reconciliationTaskEntity.setStartTime(reconciliationTaskTo.getStartTime());
        reconciliationTaskEntity.setEndTime(reconciliationTaskTo.getEndTime());

        /**
         * 扫描对账数量
         */
        Long countRes = paymentRecordDao.countBetweenTime(reconciliationTaskTo.getStartTime(), reconciliationTaskTo.getEndTime());
        if (countRes == null || countRes.equals(0L)) {
            throw new RRException(BizCodeEnum.NO_PAYMENT_RECORD.getMessage(), BizCodeEnum.NO_PAYMENT_RECORD.getCode());
        }

        reconciliationTaskEntity.setPaymentCount(countRes);
        reconciliationTaskEntity.setDelete(DatabaseConstant.UN_LOGISTIC_DELETED);
        reconciliationTaskEntity.setStatus(PaymentConstant.ReconciliationTaskStatus.WAITING.getCode());
        reconciliationTaskEntity.setHasErrors(PaymentConstant.ReconciliationTaskErrorStatus.UNFINISH.getCode());

        reconciliationTaskDao.insert(reconciliationTaskEntity);
    }

    /**
     * 分页查询所有对账任务
     *
     * @param pageSize 分页参数
     * @param curPage  分页参数
     * @return
     */
    @Override
    public List<ReconciliationTaskEntity> getAllReconciliationTask(Integer pageSize, Integer page) {
        List<ReconciliationTaskEntity> selectRes = reconciliationTaskDao.selectAllInPage(pageSize, pageSize * (page - 1));
        return null;
    }

    /**
     * 开始一项对账任务。任务将提交给消息队列执行
     *
     * @param taskId 任务Id
     */
    @Override
    public void startReconciliationTask(Long taskId) {
        // 乐观修改任务状态，只有一个线程能修改成功
        Long startRes = reconciliationTaskDao.changeTaskStatus(taskId,
                PaymentConstant.ReconciliationTaskStatus.WAITING.getCode(),
                PaymentConstant.ReconciliationTaskStatus.EXECUTING.getCode());
        if (startRes == null || startRes.equals(0L)) {
            throw new RRException(BizCodeEnum.RECONCILIATION_START_ERROR.getMessage(), BizCodeEnum.RECONCILIATION_START_ERROR.getCode());
        }
        rabbitTemplate.convertAndSend("payment-event-exchange", "reconciliation.task.start", taskId);
    }

    /**
     * 消息队列监听到一项对账任务的执行动作。
     *
     * @param taskId 任务Id
     */
    @Override
    public void reconciliationTaskExecute(Long taskId) {
        ReconciliationTaskEntity selectRes = reconciliationTaskDao.selectById(taskId);

        String billTableName;
        String joinTableName;
        Long errorNum;

        try {
            /**
             * 清洗第三方账单
             */
            billTableName = billRecordService.flushTradeTable(selectRes);

            /**
             * 制作本地交易记录-第三方账单join表
             */
            joinTableName = paymentBillJoinService.joinPaymentAndBillTable(selectRes);

            /**
             * 执行对账
             */
            errorNum = paymentBillJoinService.executeReconciliationOnJoinTable(selectRes);
        } catch (Exception exception) {
            /**
             * 存在异常，手动删除所有表
             */
            this.dropTablesInCurTask(selectRes);
            logger.info("对账发生错误：{}，已删除所有中间表", exception.toString());
            throw exception;
        }

        /**
         * 更新任务信息
         */
        try {
            reconciliationTaskDao.updateErrorStatus(
                    taskId,
                    (errorNum != null && errorNum > 0) ? PaymentConstant.ReconciliationTaskErrorStatus.ERRORS.getCode() :
                            PaymentConstant.ReconciliationTaskErrorStatus.NORMAL.getCode());
            reconciliationTaskDao.changeTaskStatus(taskId,
                    PaymentConstant.ReconciliationTaskStatus.EXECUTING.getCode(),
                    PaymentConstant.ReconciliationTaskStatus.FINISHED.getCode());
        } catch (Exception exception) {
            logger.info("对账已经完成，但更新任务状态时发生异常：{}", exception.toString());
            throw new RRException(BizCodeEnum.RECONCILIATION_FINISH_BUT_UPDATE_ERROR.getMessage() + ":" + exception, BizCodeEnum.RECONCILIATION_FINISH_BUT_UPDATE_ERROR.getCode());
        }

    }

    /**
     * 手动删除该对账任务下的所有表
     *
     * @param selectRes
     */
    @Override
    public void dropTablesInCurTask(ReconciliationTaskEntity selectRes) {
        billRecordDao.dropTableWithName(PaymentConstant.BILL_RECORD_PREFIX + selectRes.getId());
        paymentBillJoinDao.dropTableWithName(PaymentConstant.PAYMENT_BILL_JOIN_PREFIX + selectRes.getId());
        reconciliationErrorDao.dropTableWithName(PaymentConstant.RECONCILIATION_ERROR_PREFIX + selectRes.getId());
    }

    /**
     * 生成测试数据
     *
     * @param size
     * @param day
     */
    @Override
    public void constructTestData(Integer size, String day) {
        logger.info("制作{}条本地交易记录。记录时间为{}", size, day + " 12:00:00");
        logger.info("制作{}条第三方账单。记录时间为{}", size, day + " 12:00:00");

        try {
            Date operateDate = null;
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            operateDate = sdf.parse(day + " 12:00:00");

            int curSize = 0;

            List<PaymentRecordEntity> paymentRecordEntityList = new ArrayList<>();
            List<BillRecordEntity> billRecordEntityList = new ArrayList<>();
            String fileName = "/home/kamistoat/ubuntu/LiGuStore/tradeSource/" + day.replaceAll("-", "") + "_" + "业务明细.csv";
            String line = "";
            BufferedWriter bw = null;
            bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName)));
            bw.write("#支付宝业务明细查询\n");
            bw.write("#账号：[2088xxx0156]\n");
            bw.write("#起始日期：[xxxx年xx月xx日 00:00:00]   终止日期：[xxxx年xx月xx日 00:00:00]\n");
            bw.write("#-----------------------------------------业务明细列表----------------------------------------\n");
            bw.flush();

            int longSize = 0;
            int shortSize = 0;
            int disMatch = 0;

            while (curSize < size) {
                double random = Math.random();

                String orderSn = SNGenerate.randomOrderCode();
                String thirdTradeSn = SNGenerate.randomOrderCode();

                PaymentRecordEntity paymentRecordEntity = new PaymentRecordEntity();
                paymentRecordEntity.setOrderId(1L);
                paymentRecordEntity.setOrderSn(orderSn);
                paymentRecordEntity.setPaymentType(0);
                paymentRecordEntity.setThirdPaymentTradeSn(thirdTradeSn);
                paymentRecordEntity.setCreateTime(operateDate);
                paymentRecordEntity.setUpdateTime(operateDate);
                paymentRecordEntity.setDelete(DatabaseConstant.UN_LOGISTIC_DELETED);

                BillRecordEntity billRecordEntity = new BillRecordEntity();
                billRecordEntity.setOrderSn(orderSn);
                billRecordEntity.setThirdPaymentTradeSn(thirdTradeSn);
                billRecordEntity.setPaymentType(0);

                if (random <= 0.01) {
                    longSize++;
                    billRecordEntity.setTotalAmount(new BigDecimal("4399.00"));
                    billRecordEntityList.add(billRecordEntity);
                } else if (random > 0.01 && random <= 0.02) {
                    shortSize++;
                    paymentRecordEntity.setTotalAmount(new BigDecimal("4399.00"));
                    paymentRecordEntityList.add(paymentRecordEntity);
                } else if (random > 0.02 && random <= 0.03) {
                    disMatch++;
                    billRecordEntity.setTotalAmount(new BigDecimal("4399.00"));
                    billRecordEntityList.add(billRecordEntity);
                    paymentRecordEntity.setTotalAmount(new BigDecimal("2399.00"));
                    paymentRecordEntityList.add(paymentRecordEntity);
                } else {
                    billRecordEntity.setTotalAmount(new BigDecimal("4399.00"));
                    billRecordEntityList.add(billRecordEntity);
                    paymentRecordEntity.setTotalAmount(new BigDecimal("4399.00"));
                    paymentRecordEntityList.add(paymentRecordEntity);
                }

                if (curSize == PaymentConstant.BILL_FLUSH_SINGLE_LIMIT) {
                    logger.info("写{}条测试数据到数据库", curSize);
                    paymentRecordDao.insertBatch(paymentRecordEntityList);
                    logger.info("写{}条测试数据到第三方账单", curSize);

                    for (BillRecordEntity recordEntity : billRecordEntityList) {
                        bw.write(recordEntity.getThirdPaymentTradeSn() + "," + recordEntity.getOrderSn() + ",支付," + recordEntity.getTotalAmount());
                        bw.newLine();
                    }
                    bw.flush();

                    billRecordEntityList.clear();
                    paymentRecordEntityList.clear();
                    curSize = 0;
                    size -= PaymentConstant.BILL_FLUSH_SINGLE_LIMIT;
                }

                curSize++;
            }

            logger.info("写{}条测试数据到数据库", curSize);
            paymentRecordDao.insertBatch(paymentRecordEntityList);
            logger.info("写{}条测试数据到第三方账单", curSize);

            for (BillRecordEntity recordEntity : billRecordEntityList) {
                bw.write(recordEntity.getThirdPaymentTradeSn() + "," + recordEntity.getOrderSn() + ",支付," + recordEntity.getTotalAmount());
                bw.newLine();
            }
            bw.flush();


            bw.write("#-----------------------------------------业务明细列表结束------------------------------------\n");
            bw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }

        logger.info("测试数据生成完成");
    }
}
